#include "widget.h"
#include "ui_widget.h"

#include <arpa/inet.h>
#include <cstring>
#include <fcntl.h>
#include <iostream>
#include <netinet/in.h>
#include <sys/socket.h>
#include <unistd.h>

Widget::Widget(QWidget *parent) : QWidget(parent), ui(new Ui::Widget) {
  ui->setupUi(this);
  this->setWindowTitle("UDP客户端");
  connect(&m_timer, &QTimer::timeout, this, &Widget::onReadData);
  m_timer.setInterval(100);
  init();
}

Widget::~Widget() {
  m_timer.stop();
  delete ui;
}

void Widget::onReadData() {
  // 异步接收数据
  struct sockaddr_in client_addr{};
  socklen_t addr_len = sizeof(client_addr);

  // 接收数据
  char recvBuffer[1024];
  memset(recvBuffer, 0, sizeof(recvBuffer));
  ssize_t recvLen = recvfrom(m_udpsocket, recvBuffer, sizeof(recvBuffer) - 1, 0, (struct sockaddr *)&client_addr, &addr_len);
  if (recvLen > 0) {
      recvBuffer[recvLen] = '\0';
  } else if (recvLen == 0) {
      //对方关闭
      return ;
  } else {
      //没有数据
      return ;
  }

  QString str = QString("收到数据: %1").arg(recvBuffer);
  ui->messageShoxEdit->appendPlainText(str);
}

void Widget::init() {
    // 创建套接字socket,设置通信类型为SOCK_DGRAM 代表udp
    m_udpsocket = socket(AF_INET, SOCK_DGRAM, 0);

    // 将套接字设置为非阻塞模式,否则会阻塞界面
    int flags = fcntl(m_udpsocket, F_GETFL, 0);
    if (flags < 0) {
      perror("fcntl F_GETFL");
      ::close(m_udpsocket);
      return;
    }
    if (fcntl(m_udpsocket, F_SETFL, flags | O_NONBLOCK) < 0) {
      perror("fcntl F_SETFL");
      ::close(m_udpsocket);
      return;
    }

    m_timer.start();

}

void Widget::on_btnSend_clicked() {
   QString msg = ui->messageEdit->text();
    // 给服务端发送数据
    // 获取IP与端口
    QString ip = ui->lineEditIP->text();
    uint port = ui->lineEditPort->text().toUInt();
    msg = msg.toUtf8();
    std::string strmsg = msg.toStdString();
    //  struct sockaddr_in {
    //      sa_family_t sin_family;     // 地址族(AF_INET)
    //      in_port_t sin_port;         // 端口号
    //      struct in_addr sin_addr;    // IPv4 地址
    //      char sin_zero[8];           // 填充字节，通常设置为 0
    //  };

    //  struct in_addr {
    //      in_addr_t s_addr;
    //  };

    //  typedef __uint32_t      in_addr_t;

    //  struct sockaddr_in 成员说明：
    //  sin_family：指定地址族，对于 IPv4 地址，应该设置为 AF_INET。
    //  sin_port：指定端口号，使用 in_port_t 类型表示，需要通过 htons()
    //  函数将端口号从主机字节序转换为网络字节序。 sin_addr：存储 IPv4
    //  地址信息，是一个 struct in_addr 类型的结构体。IPv4 地址应该通过
    //  inet_pton() 函数将字符串形式的 IP 地址转换为二进制形式。
    //  sin_zero：用于填充字节，通常设置为 0。
    struct sockaddr_in serverAddr;
    // 设置服务器地址和端口
    memset(&serverAddr, 0, sizeof(serverAddr));
    serverAddr.sin_family = AF_INET;
    serverAddr.sin_port = htons(port);
    inet_pton(AF_INET, ip.toStdString().c_str(), &(serverAddr.sin_addr));

    ssize_t sendLen = sendto(m_udpsocket, strmsg.c_str(), strmsg.size(),0, (struct sockaddr *)&serverAddr,sizeof(serverAddr));

}
